// src/components/时空演化可视化/PolygonDrawer3D.ts

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

export interface PolygonPoint {
  x: number;
  y: number;
  z: number;
}

export type DrawingCallbacks = {
  onDrawingComplete: (coordinates: PolygonPoint[]) => void;
  onDrawingCancel: () => void;
};

export class PolygonDrawer3D {
  private camera: THREE.PerspectiveCamera;
  private renderer: THREE.WebGLRenderer;
  private domElement: HTMLCanvasElement;
  private scene: THREE.Scene;
  private controls: OrbitControls;

  private onComplete: (coords: PolygonPoint[]) => void;
  private onCancel: () => void;

  private points: THREE.Vector3[] = [];
  private line?: THREE.Line;
  private tempLine?: THREE.Line;
  private mouse = new THREE.Vector2();
  private raycaster = new THREE.Raycaster();

  // 用于射线检测的隐形平面（Z-up 坐标系）
  private plane: THREE.Mesh;
  private debugPlane?: THREE.Mesh; // 添加调试平面的引用

  private enabled = false;

  // 保存控件原始状态，用来 stop 时恢复
  private savedMouseButtons: any = null;

  constructor(options: {
    camera: THREE.PerspectiveCamera;
    renderer: THREE.WebGLRenderer;
    domElement: HTMLCanvasElement;
    scene: THREE.Scene;
    controls: OrbitControls;
    callbacks: DrawingCallbacks;
    groundZ?: number;
  }) {
    this.camera = options.camera;
    this.renderer = options.renderer;
    this.domElement = options.domElement;
    this.scene = options.scene;
    this.controls = options.controls;
    this.onComplete = options.callbacks.onDrawingComplete;
    this.onCancel = options.callbacks.onDrawingCancel;

    const groundZ = options.groundZ ?? 0;

    // 主检测平面
    const PLANE_SIZE = 2000000;
    this.plane = new THREE.Mesh(
      new THREE.PlaneGeometry(PLANE_SIZE, PLANE_SIZE),
      new THREE.MeshBasicMaterial({
        visible: false,
        side: THREE.DoubleSide,
      })
    );
    this.plane.position.z = groundZ;
    this.scene.add(this.plane);

    // 可视化调试平面
    const debugMat = new THREE.MeshBasicMaterial({
      color: 0x00ff00,
      transparent: true,
      opacity: 0.15,
      side: THREE.DoubleSide,
    });
    this.debugPlane = new THREE.Mesh(
      new THREE.PlaneGeometry(PLANE_SIZE, PLANE_SIZE),
      debugMat
    );
    this.debugPlane.position.z = groundZ;
    this.scene.add(this.debugPlane);

    console.log("PolygonDrawer3D 初始化完成，地面高度 Z =", groundZ);
  }

  /** 开始绘制 */
  public start(): void {
    if (this.enabled) return;

    console.log("PolygonDrawer3D 开始绘制，接管 OrbitControls 左键");
    this.domElement.style.cursor = 'crosshair';

    // 保存原始设置并禁用左键旋转/缩放
    this.savedMouseButtons = { ...this.controls.mouseButtons };
    this.controls.mouseButtons = {
      LEFT: null,
      MIDDLE: THREE.MOUSE.PAN,
      RIGHT: THREE.MOUSE.ROTATE,
    };

    this.enabled = true;
    this.points = [];

    // 创建线条
    const lineMaterial = new THREE.LineBasicMaterial({
      color: 0x409eff,
      linewidth: 4,
    });
    const lineGeometry = new THREE.BufferGeometry();
    this.line = new THREE.Line(lineGeometry, lineMaterial);
    this.scene.add(this.line);

    const tempMaterial = new THREE.LineBasicMaterial({
      color: 0x409eff,
      opacity: 0.5,
      transparent: true,
    });
    const tempGeometry = new THREE.BufferGeometry();
    this.tempLine = new THREE.Line(tempGeometry, tempMaterial);
    this.scene.add(this.tempLine);

    // 绑定事件
    this.domElement.addEventListener("pointerdown", this.onPointerDown);
    this.domElement.addEventListener("pointermove", this.onPointerMove);
    this.domElement.addEventListener("contextmenu", this.onContextMenu);
    document.addEventListener("keydown", this.onKeyDown);

    console.log("PolygonDrawer3D 事件监听器已绑定（已接管左键）");
  }

  /** 停止并清理 */
  public stop(): void {
    if (!this.enabled) return;
    this.enabled = false;

    console.log("PolygonDrawer3D 停止绘制，恢复 OrbitControls");

    // 恢复 OrbitControls 原始左键功能
    if (this.savedMouseButtons) {
      this.controls.mouseButtons = this.savedMouseButtons;
      this.savedMouseButtons = null;
    }

    // 恢复光标样式
    this.domElement.style.cursor = "default";

    // 移除事件监听器
    this.domElement.removeEventListener("pointerdown", this.onPointerDown);
    this.domElement.removeEventListener("pointermove", this.onPointerMove);
    this.domElement.removeEventListener("contextmenu", this.onContextMenu);
    document.removeEventListener("keydown", this.onKeyDown);

    // 清理线条几何体
    this.clearLines();

    // 重置点数组
    this.points = [];

    console.log("PolygonDrawer3D 清理完成");
  }

  /** 销毁整个绘制器（完全清理） */
  public dispose(): void {
    this.stop();

    // 清理平面几何体
    if (this.plane) {
      this.scene.remove(this.plane);
      this.plane.geometry.dispose();
      (this.plane.material as THREE.Material).dispose();
    }

    // 清理调试平面
    if (this.debugPlane) {
      this.scene.remove(this.debugPlane);
      this.debugPlane.geometry.dispose();
      (this.debugPlane.material as THREE.Material).dispose();
      this.debugPlane = undefined;
    }

    console.log("PolygonDrawer3D 已完全销毁");
  }

  /** 清理线条几何体 */
  private clearLines(): void {
    // 清理主线条
    if (this.line) {
      this.scene.remove(this.line);
      this.line.geometry.dispose();
      (this.line.material as THREE.Material).dispose();
      this.line = undefined;
    }

    // 清理临时线条
    if (this.tempLine) {
      this.scene.remove(this.tempLine);
      this.tempLine.geometry.dispose();
      (this.tempLine.material as THREE.Material).dispose();
      this.tempLine = undefined;
    }
  }

  // 事件处理函数保持不变
  private onPointerDown = (event: PointerEvent): void => {
    if (event.button !== 0) return;
    if (!this.enabled) return;

    console.log("pointerdown 触发，添加点");
    this.updateMousePosition(event);
    const point = this.getIntersectPoint();
    if (!point) {
      console.warn("未检测到交点，可能是平面高度不对");
      return;
    }

    this.points.push(point);
    this.updateLine();
    console.log("已添加点，第", this.points.length, "个", point);
  };

  private onPointerMove = (event: PointerEvent): void => {
    this.updateMousePosition(event);
    if (this.points.length > 0) this.updateTempLine();
  };

  private onContextMenu = (event: MouseEvent): void => {
    event.preventDefault();
    if (this.points.length < 3) {
      this.cancel();
    } else {
      this.complete();
    }
  };

  private onKeyDown = (event: KeyboardEvent): void => {
    if (event.key === "Escape") {
      this.cancel();
    }
  };

  private updateMousePosition(event: MouseEvent | PointerEvent): void {
    const rect = this.domElement.getBoundingClientRect();
    this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
  }

  private getIntersectPoint(): THREE.Vector3 | null {
    this.raycaster.setFromCamera(this.mouse, this.camera);
    const intersects = this.raycaster.intersectObject(this.plane);
    return intersects.length > 0 ? intersects[0].point : null;
  }

  private updateLine(): void {
    if (!this.line || this.points.length === 0) return;

    const positions: number[] = [];
    this.points.forEach((p) => positions.push(p.x, p.y, p.z));

    if (this.points.length >= 3) {
      const first = this.points[0];
      positions.push(first.x, first.y, first.z);
    }

    this.line.geometry.setAttribute(
      "position",
      new THREE.Float32BufferAttribute(positions, 3)
    );
  }

  private updateTempLine(): void {
    if (!this.tempLine || this.points.length === 0) return;

    const lastPoint = this.points[this.points.length - 1];
    const currentPoint = this.getIntersectPoint();
    if (!currentPoint) return;

    const positions = [
      lastPoint.x,
      lastPoint.y,
      lastPoint.z,
      currentPoint.x,
      currentPoint.y,
      currentPoint.z,
    ];

    this.tempLine.geometry.setAttribute(
      "position",
      new THREE.Float32BufferAttribute(positions, 3)
    );
  }

  private complete(): void {
    const coordinates: PolygonPoint[] = this.points.map((p) => ({
      x: +p.x.toFixed(6),
      y: +p.y.toFixed(6),
      z: +p.z.toFixed(6),
    }));

    this.onComplete(coordinates);
    this.stop();
  }

  private cancel(): void {
    this.onCancel();
    this.stop();
  }

  /** 获取当前状态 */
  public getState(): { enabled: boolean; pointCount: number } {
    return {
      enabled: this.enabled,
      pointCount: this.points.length
    };
  }
}